
/*
 * ==========================================================================
 *
 *       Filename:  geometry.cpp
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  03/29/2010 09:48:40 AM
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  Weiyu Yi 
 *        Company:  
 *
 * ==========================================================================
 */

//---------------------------------------------------------------------------
//  Includes
//---------------------------------------------------------------------------

#include <math.h>
#include <ColUtils.h>
#include <ColUtilsOpenGL.h>

using col::Point3;
using col::Vector3;
using col::Matrix4;
using col::ColGeometry;
using col::Primitive;
using col::Cube;
using col::Icosahedron;
using col::Trackball;

namespace
{

typedef enum
{
	PERSPECTIVE,
	ORTHOGRAPHIC	
} ProjectionE;

typedef enum
{
	MODELING,
	VIEWING
} ControlModeE;

// here declaration of the global variables.
ColGeometry * geo = NULL;
bool with_windows = true;

// render options
GLuint w, h;
ControlModeE ctrol = VIEWING;
ProjectionE proj = PERSPECTIVE;
GLenum fill_mode = GL_LINE;
GLdouble color[3] = { 0.9, 0.9, 0.9 };
Trackball tball;

GLdouble view_distance = 3.0;
Matrix4 moving_matrix;
Matrix4 viewing_matrix;

}

void testcase( int *phase )
{
	printf("\nphase %d\n", *phase );
	switch ( *phase )
	{
		case 0: // Cube
		{
			if ( geo != NULL ) 
			{
				delete geo;
				geo = NULL;
			}
			geo = new ColGeometry();
			col::makeCube( geo, 0.6 );

			break;
		}
		case 1: // Icosahedron 
		{
			if ( geo != NULL )
			{
				delete geo;
				geo = NULL;
			}
			geo = new ColGeometry();
			col::makeSphere( geo, 0, 1 );

			break;
		}
		case 2: // makeSphere
		{
			if ( geo != NULL )
			{
				delete geo;
				geo = NULL;
			}

			geo = new ColGeometry();
			col::makeSphere( geo, 2, 0.5 );
			
			break;
		}
		case 3: // Load Geometry
		{
			if ( geo != NULL )
			{
				delete geo;
				geo = NULL;
			}

			geo = new ColGeometry();
            std::string filepath( FILE_LOAD_PATH );
            std::string filename( "pin.obj" );
			col::loadGeometryFromFile( geo, ( filepath+filename ).c_str() );
			//col::loadGeometryFromFile( geo, "../../../coll/wrapper/wrapper_opengl/test/Data/pin.obj" );
			break;
		}
		case 4: // Load Geometry
		{
			if ( geo != NULL )
			{
				delete geo;
				geo = NULL;
			}

			geo = new ColGeometry();
            std::string filepath( FILE_LOAD_PATH );
            std::string filename( "ball.obj" );
			col::loadGeometryFromFile( geo, ( filepath+filename ).c_str() );
			//col::loadGeometryFromFile( geo, "../../../coll/wrapper/wrapper_opengl/test/Data/ball.obj" );
			break;
		}
		case 5: // makePlane
		{
			if ( geo != NULL )
			{
				delete geo;
				geo = NULL;
			}

			geo = new ColGeometry();
			col::makePlane( geo, 1.0, 1.0, 5, 5 );
			
			break;
		}
		default:
			printf( "last phase has been reached; starting again.\n" );
			*phase = -1;
	}
	(*phase) ++;
}

void init(void) 
{
	glClearColor( 0.0, 0.0, 0.0, 0.0 );
	glShadeModel( GL_FLAT );
	glEnable( GL_DEPTH_TEST );
}

void reshape( int width, int height )
{
	w = width;
	h = height;
	glViewport( 0, 0, (GLsizei) w, (GLsizei) h );

	tball.setViewport( width, height );

}

void display(void)
{
	// trackball
	if ( ctrol == VIEWING )
	{
		if ( tball.getTrackMode() == col::TRACK_ROTATE )
		{
			REAL angle;
			REAL axis[3];
			tball.getRotation( axis, angle );
			printf( "angle = %f\n", angle );
			printf( "axis is %f,%f,%f\n",axis[0], axis[1], axis[2] );
			Vector3 ax( axis[0], axis[1], axis[2] );
			Matrix4 m = Matrix4::rotation( angle, ax );
			viewing_matrix = m * viewing_matrix;
		}
		else
		if ( tball.getTrackMode() == col::TRACK_ZOOM )
		{
			REAL zoom;
			tball.getZoom( zoom );
			Vector3 zm( zoom+1 );
			Matrix4 m = Matrix4::scale( zm );
			viewing_matrix = m * viewing_matrix;
		}
	}

	// display begins
	glMatrixMode( GL_PROJECTION );
	glLoadIdentity();

	GLdouble range=2.0;
	switch( proj )
	{
		case ORTHOGRAPHIC:
			// orthographic projection
			if (w <= h)
				glOrtho( -1*range, range, -1*range*(GLdouble)h/(GLdouble)w,
						range*(GLdouble)h/(GLdouble)w, 0.1, 10.0 );
			else
				glOrtho( -1*range*(GLdouble)w/(GLdouble)h,
						range*(GLdouble)w/(GLdouble)h, -1*range, range, 0.1, 10.0 );
			break;
		case PERSPECTIVE:
			// perspective projection
			gluPerspective( 60, (GLdouble)w/(GLdouble)h, 0.1, 10.0 );
			break;
		default:
			fprintf( stderr, "No such projection mode!\n" );
			break;
	}

	glMatrixMode( GL_MODELVIEW );
	glLoadIdentity();
	glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );

	// View Transformation
	glTranslatef( 0.0, 0.0, -view_distance );


	GLdouble mat[16];
	for ( unsigned int i=0; i<4; i++ )
		for ( unsigned int j=0; j<4; j++ )
			mat[ i*4+j ] = viewing_matrix[i][j];
	glMultMatrixd( mat );

	// debug...
	for ( unsigned int i=0; i<16; i++ )
		printf( "%f,", mat[i] );
	printf( "\n" );
	
	// Modelling Transformation
	//
	
	// draw geometry
	if ( geo != NULL )
	{
		col::drawGeometry( geo, fill_mode, color );
	}
	
	//
	glutSwapBuffers();
}

void mouseClick( int button, int state, int x, int y )
{
	printf( "x=%d, y=%d\n", x, y );
	tball.mouseClick( button, state, x, y );
}

void mouseMoving( int x, int y )
{
	tball.mouseMoving( x, y );
	glutPostRedisplay();
}

void keyboard(unsigned char key, int x, int y)
{
	static int phase = 0;
	switch (key) {
		case 27:
			if ( geo != NULL )
				delete geo;
			exit(0);
			break;
		case 'm':
		case 'M':
			if ( ctrol == VIEWING )
				ctrol = MODELING;
			else
			if ( ctrol == MODELING )
				ctrol = VIEWING;

			glutPostRedisplay();
			break;
		case ' ':
			testcase( &phase );

			glutPostRedisplay();
			break;
		case 'p':
		case 'P':
			if ( proj == PERSPECTIVE )
				proj = ORTHOGRAPHIC;
			else
			if ( proj == ORTHOGRAPHIC )
				proj = PERSPECTIVE;

			glutPostRedisplay();
			break;
		case 'i':
		case 'I':
			if ( fill_mode == GL_LINE )
				fill_mode = GL_FILL;
			else
			if ( fill_mode == GL_FILL )
				fill_mode = GL_POINT;
			else
			if ( fill_mode == GL_POINT )
				fill_mode = GL_LINE;

			glutPostRedisplay();
			break;
		default:
			break;
	}
}

/***************************************************************************\
 *                              Main                                       *
\***************************************************************************/

void commandlineerror( char *cmd, char *parm )
{
	if ( cmd )
		fprintf(stderr, "Offending option: %s\n", cmd );
	if ( parm )
		fprintf(stderr, "with first parameter: %s\n", parm );

	fprintf(stderr, "\n\nUsage: geometry options ...\n"
	"Options:\n"
	"-h          show help\n"
	"Keys: (interactive version)\n"
	"i           polygon mode (wireframe, dots, filled)\n"
	"space       go to the next test case\n"
	"ESC         quit\n"
	"\n");

	if ( cmd )
		exit(-1);				// problem occured
	else
		exit(0);				// was option -h
}


void parsecommandline( int argc, char *argv[] )
{
	/* valid option characters; last char MUST be 0 ! */
	char optionchar[] =   { 'h', 'w', '\0' };
	int musthaveparam[] = {  0 ,  0  };
	int mhp[256];
	int isopt[256];
	char optchar;


	unsigned int nopts = strlen(optionchar);
	if ( nopts > 50 )
	{
		fprintf(stderr, "\n\nparsecommandline: the option-chars string "
				"seems to be\nVERY long (%d bytes) !\n\n", nopts );
		exit(-1);
	}

	fill_n( isopt, 256, 0 );
	fill_n( mhp, 256, 0 );
	for ( unsigned int i = 0; i < nopts; i ++ )
	{
		if ( isopt[static_cast<int>(optionchar[i])] )
		{
			fprintf(stderr, "\n\nparsecommandline: Bug: option character '%c'"
					" is specified twice in the\n"
							"option character array !\n\n", optionchar[i] );
			exit(-1);
		}
		isopt[ static_cast<int>(optionchar[i]) ] = 1;
		mhp[ static_cast<int>(optionchar[i])] = musthaveparam[i];
	}

	++argv; --argc;
	while ( argc > 0 )
	{
		if ( argv[0][0] == '-' )
		{
			optchar = argv[0][1];

			if ( ! isopt[static_cast<int>(optchar)] )
			{
				fprintf(stderr, "\nIs not a valid command line option\n");
				commandlineerror( argv[0], NULL );
			}
			for ( int i = 0; i < mhp[static_cast<int>(optchar)]; i ++ )
				if ( ! argv[1+i] || argv[1+i][0] == '-' )
				{
					fprintf(stderr, "\nCommand line option -%c must "
							"have %d parameter(s)\n",
							argv[0][1], mhp[static_cast<int>(optchar)] );
					commandlineerror( argv[0], NULL );
					argv += 1 + i;
					argc -= 1 + i;
					continue;
				}

			switch ( optchar )
			{
				case 'h': commandlineerror( NULL, NULL);  break;
				default: fprintf(stderr, "\nBug in parsecommandline !\n");
						 commandlineerror( argv[0], NULL );
			}

			argv += 1 + mhp[static_cast<int>(optchar)];
			argc -= 1 + mhp[static_cast<int>(optchar)];
		}
		else
		{
			/* command line arg doesn't start with '-' */
			fprintf(stderr, "\nThis is not a valid command line option\n");
			commandlineerror( argv[0], NULL );
			/* or, load file instead .. */
		}
	}
}


int main(int argc, char** argv)
{
	// parse command line options
	parsecommandline( argc, argv );

	// preparing data
	viewing_matrix = Matrix4::identity();
	moving_matrix = Matrix4::identity();

	glutInit( &argc, argv );
	glutInitDisplayMode ( GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGB );

	glutInitWindowSize( 350, 350 ); 
	glutInitWindowPosition( 100, 100 );
	glutCreateWindow( argv[0] );

	init();

	// register callback to glut
	glutDisplayFunc( display ); 
	glutReshapeFunc( reshape );

	glutMouseFunc( mouseClick );
	glutMotionFunc( mouseMoving );
	glutKeyboardFunc( keyboard );

	//
	glutMainLoop();

	return 0;
}
